﻿using System;
using System.Threading;
using System.Net;
using System.Net.Sockets;

namespace OutOfUnity
{
    public class TcpSocket : ISocket
    {
        private Socket socket_ = null;
        private String error_ = "";
        private ManualResetEvent connectTimeoutEvent_ = null;
        private Boolean connected_ = false;
        private String ip_ = "";
        private int port_ = 0;
        public void update()
        {
            return;
        }
        public bool connect(String ip, int port)
        {
            ip_ = ip;
            port_ = port;
            connectTimeoutEvent_ = new ManualResetEvent(false);
            socket_ = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket_.NoDelay = true;
            socket_.ReceiveTimeout = Config.TCP_RECV_TIMEOUT;
            socket_.ReceiveBufferSize = Config.RECV_BUF_SIZE;
            socket_.SendBufferSize = Config.SEND_BUF_SIZE;
            connectTimeoutEvent_.Reset();
            try
            {
                socket_.BeginConnect(new IPEndPoint(IPAddress.Parse(ip_), port_),
                    new AsyncCallback(connectCallback), socket_);
            }
            catch (System.Exception e)
            {
                error_ = e.Message;
                return false;
            }
            if (!connectTimeoutEvent_.WaitOne(Config.CONNECT_TIMEOUT, false))
            {
                socket_.Close();
            }
            return connected_;
        }
        private void connectCallback(IAsyncResult asyncResult)
        {
            connected_ = true;
            try
            {
                socket_.EndConnect(asyncResult);
            }
            catch (SystemException e)
            {
                connected_ = false;
                error_ = e.Message;
                return;
            }
            finally
            {
                connectTimeoutEvent_.Set();
            }
        }
        public bool close()
        {
            error_ = "";
            if (socket_ != null)
            {
                socket_.Close();
                socket_ = null;
            }
            connectTimeoutEvent_ = null;
            connected_ = false;
            return true;
        }
        public int send(byte[] buffer, int size)
        {
            try
            {
                var sentBytes = socket_.Send(buffer, size, 0);
                if (sentBytes <= 0)
                {
                    return -1;
                }
                return sentBytes;
            }
            catch (System.Exception e)
            {
                error_ = e.Message;
                return -1;
            }
        }
        public int receive(byte[] buffer)
        {
            int bytes = 0;
            try
            {
                bytes = socket_.Receive(buffer);
                if (bytes == 0)
                {
                    return -1;
                }
                return bytes;
            }
            catch (SocketException)
            {
                return 0;
            }
            catch (System.IO.IOException e)
            {
                error_ = e.Message;
                return -1;
            }
        }
        public String getError()
        {
            return error_;
        }
        public bool isConnect()
        {
            return connected_;
        }
    }
}
